
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.seari.commonutils.redis;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.exceptions.JedisConnectionException;

/**
 *
 * @author Rainbow
 */
//哨兵REDIS工具类
public class JedisSentinelUtil
{
	
	//日志
	protected static Logger logger = LoggerFactory.getLogger(JedisSentinelUtil.class);

    public static volatile JedisSentinelPool jedisPool = null;

    public JedisSentinelUtil(Set<String> sentinels, String masterName)
    {
        init(sentinels, masterName);
    }

    public synchronized static void init(Set<String> sentinels, String masterName)
    {
        System.out.println("init jedis sentinel pool");
        if (jedisPool == null)
        {
            // redis 属性配置
            JedisPoolConfig config = new JedisPoolConfig();
            config.setBlockWhenExhausted(true); // 连接耗尽则阻塞
            config.setLifo(true); //后进先出
            config.setMaxTotal(5000); // 最大连接数
            config.setMaxIdle(256);  // 最小空闲连接数
            config.setMaxWaitMillis(-1); // 设置最大等待毫秒数，小于0时为阻塞不确定的时间
            config.setTestOnBorrow(true); // 获取连接时是否检查连接的有效性：是
            config.setTestOnReturn(true); // 归还连接时是否检查连接的有效性：是
            config.setTestWhileIdle(true); // 空闲时是否检查连接的有效性：是
            config.setMinEvictableIdleTimeMillis(600000L); // 逐出连接的最小空闲时间：10分钟
            config.setTimeBetweenEvictionRunsMillis(30000L); //逐出扫描的时间间隔(毫秒) 30秒
            config.setNumTestsPerEvictionRun(5); //每次逐出检查时 逐出的最大数目 5条

            Set<String> sentinelset = sentinels;
            //sentinels.add("80.27.112.118:26379"); // 此处放置ip及端口为 sentinel
            // 服务地址，如果有多个sentinel 则逐一add即可
            jedisPool = new JedisSentinelPool(masterName, sentinelset, config);
        }
    }

    public static Jedis getJedis()
    {
        return jedisPool.getResource();
    }

    public static String get(String key)
    {
        String value = null;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            value = jedis.get(key);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
        return value;
    }

    public static void close(Jedis jedis)
    {
        try
        {
        	//jedisPool.close();
            jedisPool.returnResource(jedis);
        } catch (Exception e)
        {
            if (jedis.isConnected())
            {
                jedis.quit();
                jedis.disconnect();
            }
        }
    }

    public static byte[] get(byte[] key)
    {
        byte[] value = null;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            value = jedis.get(key);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }

        return value;
    }

    public static void set(String key, String value)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static void set(String key, String value, int time)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
            jedis.expire(key, time);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static void set(byte[] key, byte[] value, int time)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.set(key, value);
            jedis.expire(key, time);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static void hset(byte[] key, byte[] field, byte[] value)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.hset(key, field, value);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static void hset(String key, String field, String value)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.hset(key, field, value);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static String hget(String key, String field)
    {
        String value = null;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            value = jedis.hget(key, field);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }

        return value;
    }

    public static byte[] hget(byte[] key, byte[] field)
    {
        byte[] value = null;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            value = jedis.hget(key, field);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }

        return value;
    }

    public static void hdel(byte[] key, byte[] field)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.hdel(key, new byte[][]
            {
                field
            });
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static void lpush(String key, String value)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.lpush(key, new String[]
            {
                value
            });
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static void rpush(byte[] key, byte[] value)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.rpush(key, new byte[][]
            {
                value
            });
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static void rpoplpush(byte[] key, byte[] destination)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.rpoplpush(key, destination);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static List<byte[]> lpopList(byte[] key)
    {
        List list = null;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            list = jedis.lrange(key, 0L, -1L);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }

        return list;
    }

    public static String rpop(String key)
    {
        String bytes = null;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            bytes = jedis.rpop(key);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }

        return bytes;
    }

    public static List<byte[]> lrange(byte[] key, int from, int to)
    {
        List result = null;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            result = jedis.lrange(key, from, to);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }

        return result;
    }

    public static void del(String key)
    {
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.del(key);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
    }

    public static long llen(byte[] key)
    {
        long len = 0L;
        Jedis jedis = null;
        try
        {
            jedis = jedisPool.getResource();
            jedis.llen(key);
        } catch (Exception e)
        {
            jedisPool.returnBrokenResource(jedis);
            e.printStackTrace();
        } finally
        {
            close(jedis);
        }
        return len;
    }
    
    /**
     * 从 Sentinel 集群中获取 Master 名称列表
     * Set value format is ip:port
     */
    public static Set<String> getMasterNames(Set<String> sentinels) {

        if (sentinels == null || sentinels.isEmpty()) {
            return Collections.emptySet();
        }

        Set<String> masterNames = new LinkedHashSet<String>();
        Iterator<String> it = sentinels.iterator();
        Jedis sentinel = null;
        String shap = null;

        while (it.hasNext()) {
            shap = it.next();
            try {
                final HostAndPort hap = toHostAndPort(Arrays.asList(shap.split(":")));

                sentinel = new Jedis(hap.getHost(), hap.getPort());

                String sInfo = sentinel.info();

                if (StringUtils.isNotBlank(sInfo)) {
                    InputStream is = new ByteArrayInputStream(sInfo.getBytes());
                    Properties p = new Properties();
                    p.load(is);

                    // check master number
                    int masterN = Integer.parseInt(p.getProperty("sentinel_masters"));

                    if (masterN > 0) {
                        String val = null;
                        String status = null;
                        String masterName = null;
                        for (int i = 0; i < masterN; i++) {
                            val = p.getProperty("master" + i);
                            String[] kv = val.split(",");

                            status = kv[1].split("=")[1];
                            masterName = kv[0].split("=")[1];

                            // Master status=ok
                            if ("ok".equals(status)) {
                                masterNames.add(masterName);
                            }
                            logger.info("masterName={}, status={}", masterName, status);
                        }
                    } else {
                    	logger.info("Not found any master in sentinel {}", shap);
                    }
                } else {
                	logger.error("Sentinel command execute fail, result is blank.");
                }
            } catch (JedisConnectionException ce) {
                sentinels.remove(shap);
                logger.error(String.format("[%s] connection exception, cause by: ", shap), ce);
            } catch (Exception e) {
            	logger.error(String.format("[%s] runtime exception, cause by: ", shap), e);
            } finally {
                sentinel.close();
            }
        }
        return masterNames;
    }

    private static HostAndPort toHostAndPort(List<String> hostAndPort) {
        return new HostAndPort(hostAndPort.get(0), Integer.parseInt(hostAndPort.get(1)));
    }

    
}

